package com.swing.sky.web.generator.util;

import com.google.common.base.CaseFormat;
import com.swing.sky.web.generator.domain.Column;
import com.swing.sky.web.generator.domain.Module;
import com.swing.sky.web.generator.domain.GenColumn;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 数据库与代码之间的映射
 *
 * @author swing
 */
public class MapUtils {

    public static final String BOOLEAN_PREFIX = "is_";
    public static final String UNDER_SCORE = "_";

    /**
     * 过滤DO的公共字段
     */
    public static List<GenColumn> filterBasicColumns(List<GenColumn> genColumns) {
        String basicColumns = Configs.readSky("basicColumns");
        String[] split = basicColumns.split(",");
        List<String> strings = Arrays.asList(split);
        return genColumns.stream().filter(a -> !strings.contains(a.getPropertyName())).collect(Collectors.toList());
    }

    /**
     * 获取DO的公共字段
     */
    public static List<GenColumn> getBasicColumns(List<GenColumn> genColumns) {
        String basicColumns = Configs.readSky("basicColumns");
        String[] split = basicColumns.split(",");
        List<String> strings = Arrays.asList(split);
        return genColumns.stream().filter(a -> strings.contains(a.getPropertyName())).collect(Collectors.toList());
    }

    /**
     * 将Column转化为GenColumn
     */
    public static List<GenColumn> getGenColumnsByColumns(List<Column> columns) {
        List<GenColumn> genColumns = new ArrayList<>();
        for (Column column : columns) {
            GenColumn genColumn = new GenColumn(column);
            genColumn.setPropertyName(getPropertyByColumnName(column.getColumnName()));
            genColumn.setGetSetName(toUpperCaseName(genColumn.getPropertyName()));
            genColumn.setPropertyType(getJavaTypeByDataType(column.getDataType()));
            genColumn.setJdbcType(getJdbcTypeByDataType(column.getDataType()));
            genColumns.add(genColumn);
        }
        return genColumns;
    }

    public static List<GenColumn> getUniqueIndexsByColumns(List<Column> columns) {
        List<GenColumn> genColumns = new ArrayList<>();
        for (Column column : columns) {
            if (column.getColumnKey().equalsIgnoreCase("UNI")) {
                GenColumn genColumn = new GenColumn(column);
                genColumn.setPropertyName(getPropertyByColumnName(column.getColumnName()));
                genColumn.setGetSetName(toUpperCaseName(genColumn.getPropertyName()));
                genColumn.setPropertyType(getJavaTypeByDataType(column.getDataType()));
                genColumn.setJdbcType(getJdbcTypeByDataType(column.getDataType()));
                genColumns.add(genColumn);
            }
        }
        return genColumns;
    }

    public static List<GenColumn> getUniqueIndexsByColumns2(List<Column> columns) {
        List<GenColumn> genColumns = new ArrayList<>();
        // 如果没有唯一索引，考虑下联合主键
        for (Column column : columns) {
            if (column.getColumnKey().equalsIgnoreCase("PRI")) {
                GenColumn genColumn = new GenColumn(column);
                genColumn.setPropertyName(getPropertyByColumnName(column.getColumnName()));
                genColumn.setGetSetName(toUpperCaseName(genColumn.getPropertyName()));
                genColumn.setPropertyType(getJavaTypeByDataType(column.getDataType()));
                genColumn.setJdbcType(getJdbcTypeByDataType(column.getDataType()));
                genColumns.add(genColumn);
            }
        }
        return genColumns;
    }

    public static List<GenColumn> getCommonByColumns(List<Column> columns) {
        List<GenColumn> genColumns = new ArrayList<>();
        for (Column column : columns) {
            if (column.getColumnKey().equals("")) {
                GenColumn genColumn = new GenColumn(column);
                genColumn.setPropertyName(getPropertyByColumnName(column.getColumnName()));
                genColumn.setGetSetName(toUpperCaseName(genColumn.getPropertyName()));
                genColumn.setPropertyType(getJavaTypeByDataType(column.getDataType()));
                genColumn.setJdbcType(getJdbcTypeByDataType(column.getDataType()));
                genColumns.add(genColumn);
            }
        }
        return genColumns;
    }

    public static GenColumn getPrimaryKeyByColumns(List<Column> columns) {
        GenColumn genColumn = new GenColumn();
        for (Column column : columns) {
            if (column.getColumnKey().equalsIgnoreCase("PRI")) {
                genColumn.setPropertyName(getPropertyByColumnName(column.getColumnName()));
                genColumn.setGetSetName(toUpperCaseName(genColumn.getPropertyName()));
                genColumn.setPropertyType(getJavaTypeByDataType(column.getDataType()));
                genColumn.setJdbcType(getJdbcTypeByDataType(column.getDataType()));
                break;
            }
        }
        return genColumn;
    }

    /**
     * 根据表名完善module中的fileName属性
     */
    public static void setFileNameByTableName(String tableName, List<Module> modules) {
        // 将下划线转化为开头大写的驼峰命名
        String s = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, tableName);
        for (Module module : modules) {
            if (module.getSuffix() == null || module.getSuffix().isEmpty()) {
                String[] split = module.getTemplatePath().split("/");
                String templateName = split[split.length - 1];
                module.setFileName(templateName.split("\\.")[0]);
            } else {
                module.setFileName(s + module.getSuffix());
            }
        }
    }

    /**
     * 示例将字段user_id转化为属性userId
     */
    public static String getPropertyByColumnName(String columnName) {
        if (columnName.startsWith(BOOLEAN_PREFIX)) {
            // 依据阿里巴巴规范，是否逻辑使用在数据库中使用tinyint(1)使用is_开头，将其映射为java属性时去掉is_
            String substring = columnName.substring(3);
            return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, substring);
        }
        if (!columnName.contains(UNDER_SCORE)) {
            return columnName;
        }
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, columnName);
    }

    /**
     * 使字符串首字母大写
     */
    public static String toUpperCaseName(String name) {
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
        return name;
    }

    /**
     * 使字符串首字母大写
     */
    public static String toLowerCaseName(String name) {
        name = name.substring(0, 1).toLowerCase() + name.substring(1);
        return name;
    }

    /**
     * 解析数据库数据类型
     */
    public static String getJavaTypeByDataType(String mySqlType) {
        switch (mySqlType.toUpperCase()) {
            case "INT":
                return "Integer";
            case "BIGINT":
                return "Long";
            case "FLOAT":
                return "Float";
            case "DOUBLE":
                return "Double";
            case "BIT":
                return "Boolean";
            case "TINYINT":
                return "Boolean";
            case "SMALLINT":
                return "Short";
            case "MEDIUMINT":
            case "DECIMAL":
                return "BigDecimal";
            case "DATE":
                return "LocalDate";
            case "TIME":
                return "LocalTime";
            case "DATETIME":
            case "TIMESTAMP":
                return "LocalDateTime";
            case "YEAR":
                return "Short";
            case "CHAR":
            case "VARCHAR":
            case "TEXT":
            case "JSON":
            case "ENUM":
            case "SET":
            case "TINYTEXT":
            case "MEDIUMTEXT":
            case "LONGTEXT":
                return "String";
            case "BINARY":
            case "VARBINARY":
            case "BLOB":
            case "LONGBLOB":
                return "byte[]";
            default:
                throw new IllegalArgumentException("Unknown MySQL type: " + mySqlType);
        }
    }

    /**
     * 将数据库类型转化为jdbcType(mapper.xml中使用）
     */
    public static String getJdbcTypeByDataType(String mysqlType) {
        switch (mysqlType.toUpperCase()) {
            case "BIT":
                return "BIT";
            case "TINYINT":
                return "BOOLEAN";
            case "SMALLINT":
                return "SMALLINT";
            case "MEDIUMINT":
                return "INTEGER";
            case "INT":
            case "INTEGER":
                return "INTEGER";
            case "BIGINT":
                return "BIGINT";
            case "FLOAT":
                return "REAL";
            case "DOUBLE":
                return "DOUBLE";
            case "DECIMAL":
            case "NUMERIC":
                return "DECIMAL";
            case "DATE":
                return "DATE";
            case "TIME":
                return "TIME";
            case "YEAR":
                return "DATE";
            case "DATETIME":
                return "TIMESTAMP";
            case "TIMESTAMP":
                return "TIMESTAMP";
            case "CHAR":
                return "CHAR";
            case "VARCHAR":
            case "TEXT":
            case "TINYTEXT":
            case "MEDIUMTEXT":
            case "LONGTEXT":
                return "VARCHAR";
            case "BINARY":
            case "VARBINARY":
            case "TINYBLOB":
            case "BLOB":
            case "MEDIUMBLOB":
            case "LONGBLOB":
                return "BINARY";
            case "ENUM":
                return "VARCHAR";
            case "SET":
                return "VARCHAR";
            case "GEOMETRY":
                return "OTHER";
            case "POINT":
                return "OTHER";
            case "LINESTRING":
                return "OTHER";
            case "POLYGON":
                return "OTHER";
            case "MULTIPOINT":
                return "OTHER";
            case "MULTILINESTRING":
                return "OTHER";
            case "MULTIPOLYGON":
                return "OTHER";
            case "GEOMETRYCOLLECTION":
                return "OTHER";
            default:
                return "OTHER";
        }
    }
}
